package com.ossean.match.lucene;

import java.io.IOException;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.List;

import javax.annotation.Resource;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.wltea.analyzer.lucene.IKAnalyzer;

import com.ossean.match.dao.ProjectDao;
import com.ossean.match.dao.RelativeMemoDao;
import com.ossean.match.matchprocess.MatchIncrement;
import com.ossean.match.model.Project;
import com.ossean.match.model.RelativeMemo;
import com.ossean.match.utils.Normalizer;

@Component("luceneindex")
public class LuceneIndex {
	
	private Logger logger = LoggerFactory.getLogger(LuceneIndex.class);
	public static final String MEMOS_INDEX_PATH = "memosLuceneIndex";
	public static final String PRJS_INDEX_PATH = "prjsLuceneIndex";
	public static final String TEST_INDEX = "testIndex";
	public static String titleFieldName = "memoTitle";
	public static String memoTagsFieldName = "memoTags";
	public static String memoIdFieldName = "postId";
	public static String prjIdFieldName = "prjId";
	public static String prjNameFieldName = "prjName";
	public static String prjTagsFieldName = "prjTags";
	public static String prjSynonymsFieldName = "prjSynonyms";
	private int step = 100000;
	@Resource
	private RelativeMemoDao memoDao;
	@Resource
	private ProjectDao projectDao;
	
	public void run(MatchIncrement matchIncrement) {
		logger.info("start creating index!!!");
		long start = System.currentTimeMillis();
		Analyzer ikanalyzer = new IKAnalyzer(true);
		try {
			createPostIndex(ikanalyzer, matchIncrement);
			createPrjIndex(ikanalyzer, matchIncrement);
		} catch (IOException e) {
			logger.error("IOException: " + e);
		}
		long end = System.currentTimeMillis();
		logger.info("createIndex time: " + (end - start) / 1000 + "s");
	}

	public IndexWriter createIndexWriter(String indexPath, Analyzer analyzer)
			throws IOException {
		Directory dire = FSDirectory.open(Paths.get(indexPath));
		IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
		iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
		IndexWriter iw = new IndexWriter(dire, iwc);
		return iw;
	}

	   /**
	    * creating projects index, usually, we need not use this method, 
	    * because projects index will create by increment during matching process
	    * @param conn
	    * @param analyzer
	    * @throws IOException
	    */
	public void createPrjIndex(Analyzer analyzer, MatchIncrement matchIncrement)
			throws IOException {
		int startId = 0;
		int endId = startId + step;
		IndexWriter writer = createIndexWriter(PRJS_INDEX_PATH, analyzer);
		int maxPrjId = projectDao.getNewLast();
		while (startId < maxPrjId) {
			try {
				List<Project> projects = projectDao.getBatchPrjs(startId, endId);
				for(Project project : projects) {
					if (project.getName() == null) {
						continue;
					}
					Document doc = new Document();
					doc.add(new StringField(prjIdFieldName, String.valueOf(project.getId()), Store.YES));
					String prjName = project.getName();
					Field prjNameField = new TextField(prjNameFieldName, prjName, Store.YES);
//					if (prjName.getBytes().length == prjName.length() && prjName.replaceAll("\\d+"," ").indexOf(" ") == -1) {
//						prjNameField.setBoost(6.0f);
//					}
					doc.add(prjNameField);
					
					String prjTagsString = project.getTags();
					if (prjTagsString == null) {
						prjTagsString = "";
					}
					List<String> prjTagsList = Normalizer.tagsSegmentation(prjTagsString.toLowerCase());
					for(String prjTag : prjTagsList){
						doc.add(new StringField(prjTagsFieldName, prjTag, Store.NO));
					}
					List<String> synonymsList = Normalizer.tagsSegmentation(project.getSynonyms());
					for(String synonym : synonymsList){
						doc.add(new TextField(prjSynonymsFieldName, synonym, Store.YES));
					}
					writer.addDocument(doc);
				}
				logger.info("lucene indexed osps: "+ startId + "-->"+ endId);
			} catch (IOException e) {
				logger.error("createPrjIndex IOException: " + e);
			}
			if ((maxPrjId - endId) <= step) {
				startId = endId;
				endId = maxPrjId;
			}
			else {
				startId = endId;
				endId += step;
			}
		}
		writer.commit();
		writer.close();
	}

	/**
	 * creating post index
	 * @param conn
	 * @param analyzer
	 * @throws IOException
	 */
	public void createPostIndex(Analyzer analyzer, MatchIncrement matchIncrement)
			throws IOException {
		int startId = 0;
		int endId = startId + step;
		int lastMemoId=0;
		IndexWriter indexWriter = createIndexWriter(MEMOS_INDEX_PATH, analyzer);
		int maxMemoId = memoDao.getLastMemoId();
		while (startId < maxMemoId) {
			try {
				List<RelativeMemo> memos = memoDao.getMemoInfo(startId, endId);
				for(RelativeMemo memo : memos) {
					lastMemoId = memo.getId();
					if (memo.getTitle() == null) {
						continue;
					}
					Document doc = new Document();
					doc.add(new StringField(memoIdFieldName, String.valueOf(lastMemoId), Store.YES));
					doc.add(new TextField(titleFieldName, memo.getTitle(), Store.NO));
					String memoTagsString = memo.getTags();
					if (memoTagsString == null) {
						memoTagsString = "";
					}
					List<String> memoTagsList = Normalizer.tagsSegmentation(memoTagsString.toLowerCase());
					for(String memoTag : memoTagsList){
						doc.add(new StringField(memoTagsFieldName, memoTag, Store.NO));
					}
					indexWriter.addDocument(doc);
				}
				logger.info("lucene indexed relative_memos: "+ startId + "-->"+ endId);
			} catch (IOException e) {
				logger.error("createPostIndex IOException: " + e);
			}
			startId = endId;
			if ((maxMemoId - endId) <= step) {
				endId = maxMemoId;
			}
			else {
				endId += step;
			}
		}
		matchIncrement.setMemoHistory(lastMemoId);
		matchIncrement.writeIntoRecord();
		/*FileOutputStream fout = new FileOutputStream(new File("record.txt"));
		fout.write((0 + "\t" + lastMemoId + "\t" + 0 + "\t" + 0).getBytes());  //初始化record.txt，项目初始id为0，帖子初始id为创建索引的最后一个帖子的id
		fout.close();*/
		indexWriter.commit();
		indexWriter.close();
	}

	/**
	 * the increment of memos index
	 * @param rs
	 * @return
	 * @throws IOException
	 * @throws SQLException
	 */
	public int memoIndexIncrement(List<RelativeMemo> memos) throws IOException,
			SQLException {
		Directory dire = FSDirectory.open(Paths.get(MEMOS_INDEX_PATH));
		IndexWriterConfig iwc = new IndexWriterConfig(new IKAnalyzer(true));
		iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
		IndexWriter indexWriter = new IndexWriter(dire, iwc);
		int rsId = -1;
		for(RelativeMemo memo : memos) {
			rsId = memo.getId();
			if (memo.getTitle() == null) {
				continue;
			}
			Document doc = new Document();
			doc.add(new StringField(memoIdFieldName, String.valueOf(rsId) + "", Store.YES));
			doc.add(new TextField(titleFieldName, memo.getTitle(), Store.NO));
			String memoTagsString = memo.getTags();
			if (memoTagsString == null) {
				memoTagsString = "";
			}
			List<String> memoTagsList = Normalizer.tagsSegmentation(memoTagsString.toLowerCase());
			for(String memoTag : memoTagsList){
				doc.add(new StringField(memoTagsFieldName, memoTag, Store.NO));
			}
			indexWriter.updateDocument(new Term(memoIdFieldName, String.valueOf(rsId) + ""), doc); 
		}
		indexWriter.commit();
		indexWriter.close();
		return rsId;
	}

	/**
	 * the increment of projects index
	 * @param rs
	 * @return
	 * @throws IOException
	 * @throws SQLException
	 */
	public int prjIndexIncrement(List<Project> newPrjs) throws IOException, SQLException {
		Directory dire = FSDirectory.open(Paths.get(PRJS_INDEX_PATH));
		IndexWriterConfig iwc = new IndexWriterConfig(new IKAnalyzer(true));
		iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
		IndexWriter iw = new IndexWriter(dire, iwc);
		int rsId = -1;
		for(Project project : newPrjs) {
			rsId = project.getId();
			if (project.getName() == null || "".equals(project.getName())) {
				continue;
			}
			Document doc = new Document();
			String prjName = project.getName();
			doc.add(new StringField(prjIdFieldName, String.valueOf(rsId), Store.YES));
			Field prjNameField = new TextField(prjNameFieldName, prjName, Store.YES);
			if (prjName.getBytes().length == prjName.length() && prjName.replaceAll("\\d+"," ").indexOf(" ") == -1) {
				prjNameField.setBoost(6.0f);
			}
			doc.add(prjNameField);
			String prjTagsString = project.getTags();
			if (prjTagsString == null) {
				prjTagsString = "";
			}
			List<String> prjTagsList = Normalizer.tagsSegmentation(prjTagsString.toLowerCase());
			for(String prjTag : prjTagsList){
				doc.add(new StringField(prjTagsFieldName, prjTag, Store.NO));
			}
			List<String> synonymsList = Normalizer.tagsSegmentation(project.getSynonyms());
			for(String synonym : synonymsList){
				doc.add(new TextField(prjSynonymsFieldName, synonym, Store.YES));
			}
			iw.updateDocument(new Term(prjIdFieldName, String.valueOf(rsId) + ""), doc);  
		}
		iw.commit();
		iw.close();
		return rsId;
	}
	
	public static void main(String []args) throws IOException, ParseException {
		Directory directory = FSDirectory.open(Paths.get(TEST_INDEX));
		IndexWriterConfig iwc = new IndexWriterConfig(new IKAnalyzer(false));
		iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
		IndexWriter iw = new IndexWriter(directory, iwc);
		Document doc = new Document();
		doc.add(new StringField("prjId", String.valueOf(11), Store.YES));
		//doc.add(new TextField("name", "Google books", Store.YES));
		Field prjNameField = new TextField("name", "baidu books", Store.NO);
		prjNameField.setBoost(60.0f);
		doc.add(prjNameField);
		//doc.add(new StringField("name", "google books", Store.NO));
		//doc.add(new StringField("name", "google", Store.NO));
		iw.updateDocument(new Term("prjId",String.valueOf(11)),doc);
		
		Document doc1 = new Document();
		doc1.add(new StringField("prjId", String.valueOf(22), Store.YES));
		//doc1.add(new TextField("name", "google", Store.YES));
		Field prjNameField1 = new TextField("name", "google books", Store.NO);
		//prjNameField1.setBoost(0.5f);
		doc1.add(prjNameField1);
		List<String> synonymsList = Normalizer.tagsSegmentation("<Android SDK>,<Android UI>");
		for(String synonym : synonymsList){
			doc1.add(new TextField(prjSynonymsFieldName, synonym, Store.YES));
		}
		iw.updateDocument(new Term("prjId",String.valueOf(22)), doc1);
		iw.commit();
		iw.close();
		
		QueryParser parser = new QueryParser(prjSynonymsFieldName, new IKAnalyzer());
		IndexReader ir = DirectoryReader.open(directory);
		IndexSearcher is = new IndexSearcher(ir);
		//BooleanQuery query = new BooleanQuery();
		parser.setDefaultOperator(QueryParser.Operator.AND);
		Query query = parser.parse("android UI");
		Term term1 = new Term(prjSynonymsFieldName,"android");
		TermQuery tq1 = new TermQuery(term1);
		//query.add(tq1,BooleanClause.Occur.MUST);
		Term term = new Term(prjSynonymsFieldName, "ui");
		TermQuery tq2 = new TermQuery(term);
		//query.add(tq2, BooleanClause.Occur.MUST);
		TopDocs tds = is.search(query, 10);
		ScoreDoc[] sds = tds.scoreDocs;
		for(ScoreDoc sd : sds){
			Document d = is.doc(sd.doc);
			String[] aString = d.getValues(prjSynonymsFieldName);
			Explanation explanation = is.explain(query, sd.doc);
			System.out.println(explanation.toString());
			System.out.println(d.getField("prjId"));
			System.out.println(d.getField("name"));
			System.out.println(sd.score);
		}
		System.out.println("google books freq: " + ir.docFreq(term));
	}

}
